#ifndef TCPCLIENT_H
#define TCPCLIENT_H

#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>

#include <atomic>
#include <memory>
#include <mutex>
#include <functional>
#include <string.h>
#include <vector>
#include "TcpProtocol.h"

namespace BORNS {
/*!
 * \brief State the socket can be in
 */
enum class SocketState
{
    Invalid,       ///< Socket is initialized or setup failed
    Connected,     ///< Socket is connected and ready to use
    Disconnected,  ///< Socket is disconnected and cannot be used
    Closed         ///< Connection to socket got closed
};
/*!
 * \brief Class for TCP socket abstraction
 */
class TcpClient
{
private:
    std::atomic<int> socket_fd_;
    SocketState state_;
    std::function<void(std::vector<uint8_t> &bytes)> parseFunc_;// 解析函数
//    std::function<void(bool status)> connectStatusChangedFunc_;// 链接状态改变函数
    std::atomic<size_t> uniqueId_;// 唯一识别this client的id
    TcpProtocol::Type proType_;// 协议种类
    TcpProtocol::Device dev_;// 设备种类
    std::string hostIp_;// 服务端ip
    int port_;// 服务端端口
    std::atomic_bool selectTimeoutError_;// 不检测select超时错误标志
    std::atomic_bool rcvThreadRunning_;// 接收线程运行标志
    std::atomic_bool pauseRcvThread_;// 暂停接收线程运行标志
    std::atomic_bool rcvThreadIsPause_;// 接收线程暂停状态
protected:
    virtual void setOptions(int socket_fd);


    std::unique_ptr<timeval> recv_timeout_;

public:
    /*!
   * \brief Creates a TcpClient object
   * \param[in] 是否检测select超时错误标志
   * \param[in] 拆包协议 默认无拆包机制且必须是客户端、服务端同时有拆包协议才可用
   * \param[in] 设备种类
   */
    TcpClient(bool selectTimeout = false,
              TcpProtocol::Type p = TcpProtocol::Type::Default,
              TcpProtocol::Device dev = TcpProtocol::Device::Unknown);
    virtual ~TcpClient();

    /*!
   * \brief 与服务端建立TCP链接
   *
   * \param[in] 服务端ip
   * \param[in] 服务端端口号
   * \param[in] 拆包协议
   *
   * \returns 链接成功返回true
   */
    bool Connect(const std::string& host, int port);
    /*!
   * \brief 与客户端断开TCP链接
   */
    void Disconnect();
    /*!
   * \brief 与客户端重新链接 该接口必须具备重连属性才可用
   *
   * \returns 链接成功返回true
   */
    bool Reconnect();
    /*!
   * \brief Getter for the state of the socket.
   *
   * \returns Returns the current state of the socket
   */
    SocketState GetState() const
    {
        return state_;
    }

    /*!
   * \brief Getter for the file descriptor of the socket.
   *
   * \returns The file descriptor of the socket
   */
    int GetSocketFD()
    {
        return socket_fd_;
    }
    /*!
   * \brief Setter for the file descriptor of the socket.
   *
   * \param socket_fd The new value
   *
   * \returns False, if the socket is in state connected
   */
    bool SetSocketFD(int socket_fd);

    /*!
   * \brief Determines the IP address of the local machine
   *
   * \returns The IP address of the local machine.
   */
    std::string GetIP() const;

    /*!
   * \brief Reads one byte from the socket
   *
   * \param[out] character Target buffer
   *
   * \returns True on success, false otherwise
   */
    bool Read(char* character);

    /*!
   * \brief Reads data from the socket
   *
   * \param[out] buf Buffer where the data shall be stored
   * \param[in] buf_len Number of bytes allocated for the buffer
   * \param[out] read Number of bytes actually read
   *
   * \returns True on success, false otherwise
   */
    bool Read(uint8_t* buf, size_t buf_len, size_t& read);
    bool Read(std::vector<uint8_t> &buf);
    bool Read(std::string &str);
    /*!
   * \brief Writes to the socket
   *
   * \param[in] buf Buffer of bytes to write
   * \param[in] buf_len Number of bytes in the buffer
   *
   * \returns True on success, false otherwise
   */
    bool Write(const uint8_t* buf, size_t buf_len);
    bool Write(const std::vector<uint8_t> &buf);
    /*!
   * \brief Writes to the socket
   *
   * \param[in] buf Buffer of bytes to write
   *
   * \returns True on success, false otherwise
   */
    bool Write(const void *buf, size_t buf_Len);
    /*!
   * \brief Writes to the socket
   *
   * \param[in] string to write
   *
   * \returns True on success, false otherwise
   */
    bool Write(const std::string &str);

    /*!
   * \brief 停止接收线程
   */
    void StopReceiveThread();

    /*!
   * \brief 暂停接收线程(阻塞函数，可能会睡眠)
   */
    void PauseReceiveThread();
    /*!
   * \brief 激活接收线程
   */
    void EnableReceiveThread();
    /*!
   * \brief Setup Receive timeout used for this socket.
   *
   * \param[in] timeout Timeout used for setting things up
   */
    void SetReceiveTimeout(const timeval& timeout);
    /*!
   * \brief 调用该接口的前提必须是已成功建立链接，否则该函数返回false
   * \brief 非TcpProtocol::Type::HeaderData协议：调用者必须负责删除已解析的数据缓存
   * \brief 开启接收线程：按照应用协议需要的数据长度接收数据包，并回调解析函数
   *
   * \param[in] 解析函数
   */
    bool StartReceiveThread(std::function<void(std::vector<uint8_t> &bytes)> parseFunc);
    /*!
   * \brief 运行接收线程
   */
    void RcvThread();
    /*!
   * \brief 阻塞接收id
   */
    size_t RcvId();
//    /*!
//   * \brief 设置重连成功回调
//   */
//    void SetConnectStatusChangedCallback(std::function<void(bool status)> statusChangedFunc);
private:
    /*!
   * \brief Closes the connection to the socket.
   */
    void Close();
    /*!
   * \brief 阻塞式重连：重连成功才会返回
   */
    void ReconnectBlock();
    /*!
   * \brief Writes to the socket
   *
   * \param[in] buf Buffer of bytes to write
   * \param[in] buf_len Number of bytes in the buffer
   * \param[out] written Number of bytes actually written
   *
   * \returns True on success, false otherwise
   */
    bool Write(const uint8_t* buf, size_t buf_len, size_t& written);
    /*!
   * \brief 拆包：取前4个byte->int packageSize 根据packageSize拆包
   *
   * \param[in] 解析的buffer
   */
    void Unpack(std::vector<uint8_t> &bytes);
    /*!
   * \brief 解析buffer的前4个byte->int id
   *
   * \param[in] 解析的buffer
   * \param[in] 解析的id值
   *
   * \return 解析成功返回true，失败返回false
   */
    bool UnpackId(std::vector<uint8_t> &bytes, size_t &id);
};

}//namespace BORNS
#endif // TCPCLIENT_H
